fix(manager): optimize video coverage query (22-47s → <1s)#637
Merged
fix(manager): optimize video coverage query (22-47s → <1s)#637
Conversation
…sion - Add /api/video-coverage custom endpoint with SQL aggregation for per-video coverage counts (subtitles + audio, human vs AI) - Revert maxLimit: 100 from GraphQL config (broke pageSize: 5000, caused 11-page sequential fetching instead of 1) - Increase language cache TTL from 5min to 24h (geo data rarely changes) The new endpoint benchmarks at 60-660ms vs 22-47s for the current GraphQL approach of fetching 414K variant rows through the ORM.
Replace the 414K-row GraphQL fetch (22-47s) with a call to the new
/api/video-coverage CMS endpoint (60-660ms SQL aggregation).
- Coverage counts { human, ai, none } per video per coverage type
- Collections show own coverage, standalone videos returned separately
- Language-filtered requests hit CMS directly (fast enough at ~60ms)
- Global requests use SWR cache (2min TTL)
- Remove GraphQL query, fetchAllPages, and JS-side coverage computation
Update CmsVideo/CmsCollection types to use { human, ai, none } counts
instead of single status strings. Derive CoverageStatus from counts
via countsToStatus() for existing UI components.
Note: pre-existing eslint rule definition errors (react-hooks/set-state-in-effect,
@next/next/no-img-element) unrelated to this change.
|
🚅 Deployed to the forge-pr-637 environment in forge
1 service not affected by this PR
|
The collection (e.g. feature film, series) now appears as the first tile in its grid, showing its own coverage status. Distinguished from children by a thicker border and rounded corners. In expanded view, labeled with "(collection)" suffix.
…ndalone Collections are now sorted alphabetically by title in the API response. Standalone videos group is always last and no longer injects a synthetic collection tile as the first square.
Tile hover now shows: "Title — Human (2 human, 1 AI, 0 none)" instead of just "Title — Human".
Display "X human, Y AI, Z none" counts in the bottom hover bar next to the status label. Subtle styling to not overwhelm.
Replace single status label with colored pills: - Green: "X Verified subtitles" (only if > 0) - Purple: "X AI subtitles" (only if > 0) - Red: "X No subtitles" (only if > 0) Label adapts to active report type (subtitles/audio/meta). In global mode (no language filter), "none" count is computed as total languages minus human minus AI.
The pill X button was only updating draft state without navigating, so removing a language didn't trigger a data refetch. Now it updates both the draft state and the URL params immediately.
ModeToggle now renders for subtitles, audio, and meta report types. Translate button is greyed out with "Coming soon" tooltip on audio and meta. Switching to audio/meta while in translate mode auto-resets to explore.
Replace native title attr (doesn't work on disabled buttons) with a CSS ::after pseudo-element tooltip that appears above the button on hover showing "Coming soon".
Native <select> styling is inconsistent across browsers. Replace with a custom button+menu dropdown following the ReportTypeSelector pattern. Closes on Escape or outside click.
Filter collections by type (Series, Feature Film, Standalone, etc.) using a dropdown next to the coverage segment filter. Standalone is included as an option. Clear filters resets all three filters.
Replace plain "No videos match this filter" text with a centered empty state showing a search icon, "No results found" title, and a hint to adjust search/filters.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This was referenced Apr 2, 2026
tataihono
added a commit
that referenced
this pull request
Apr 2, 2026
- Add eslint-plugin-react-hooks v7 at root to resolve set-state-in-effect rule used in inline eslint-disable comments - Register plugin in eslint.config.mjs for tsx/ts files - Fix prettier formatting lost during #637 squash merge - Add CLAUDE.md rule: never skip pre-commit hooks Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
tataihono
added a commit
that referenced
this pull request
Apr 2, 2026
- Add eslint-plugin-react-hooks v7 and @next/eslint-plugin-next at root so local eslint matches CI (resolves set-state-in-effect, no-img-element) - Register both plugins in eslint.config.mjs for tsx/ts files - Fix prettier formatting lost during #637 squash merge - Restore eslint-disable comment for no-img-element on <img> tag - Add CLAUDE.md rule: never skip pre-commit hooks Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
tataihono
added a commit
that referenced
this pull request
Apr 2, 2026
The root eslint config was missing react-hooks and @next/next plugins that CI has via eslint-config-next. This caused eslint-disable comments referencing set-state-in-effect and no-img-element to error locally as "Definition for rule not found", blocking all commits touching manager. - Add eslint-plugin-react-hooks v7 and @next/eslint-plugin-next to root - Register plugins in eslint.config.mjs for tsx/ts files - Fix prettier formatting lost during #637 squash merge - Add CLAUDE.md rule: never skip pre-commit hooks Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
tataihono
added a commit
that referenced
this pull request
Apr 2, 2026
The root eslint config was missing react-hooks and @next/next plugins that CI has via eslint-config-next. This caused eslint-disable comments referencing set-state-in-effect and no-img-element to error locally as "Definition for rule not found", blocking all commits touching manager. - Add eslint-plugin-react-hooks v7 and @next/eslint-plugin-next to root - Register plugins in eslint.config.mjs for tsx/ts files - Fix prettier formatting lost during #637 squash merge - Add CLAUDE.md rule: never skip pre-commit hooks Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
tataihono
added a commit
that referenced
this pull request
Apr 2, 2026
* fix(manager): fix lint errors and add no-verify CLAUDE.md rule Remove eslint-disable/enable comments referencing undefined rules (react-hooks/set-state-in-effect, @next/next/no-img-element) and fix blank lines left behind. Add CLAUDE.md rule to never skip hooks. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add react-hooks and next eslint plugins to root config The root eslint config was missing react-hooks and @next/next plugins that CI has via eslint-config-next. This caused eslint-disable comments referencing set-state-in-effect and no-img-element to error locally as "Definition for rule not found", blocking all commits touching manager. - Add eslint-plugin-react-hooks v7 and @next/eslint-plugin-next to root - Register plugins in eslint.config.mjs for tsx/ts files - Fix prettier formatting lost during #637 squash merge - Add CLAUDE.md rule: never skip pre-commit hooks Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/videosfetched 414K variant rows + 20K subtitle rows through Strapi GraphQL to compute per-video coverage. This took 22-47s per cache refresh, blocking auth checks and causing session timeouts./api/video-coverage) computes coverage via SQL aggregation (60-660ms). Manager calls this instead of GraphQL.maxLimit: 100from fix(cms): fix production PG pool exhaustion causing KnexTimeoutErrors #626 (brokepageSize: 5000pagination), increases language cache TTL from 5min to 24h.Changes
apps/cms/src/api/video-coverage/apps/cms/config/plugins.tsmaxLimit: 100regressionapps/manager/src/app/api/videos/route.tsapps/manager/src/features/coverage/coverage-report-client.tsx{ human, ai, none }countsapps/manager/src/app/api/languages/route.tsBenchmark (production data)
Test plan
/api/video-coveragereturns all ~1083 videos with coverage counts/api/video-coverage?languageIds=529returns English-filtered countsPost-Deploy Monitoring & Validation
@forge/cmsand@forge/manager[video-cache]and[api/videos]log linesKnexTimeoutErrororCannot return nullerrors. Cache refresh logs show <2s. NoInternal Server Errorflood.[video-cache] Background refresh failedin manager logs → rollbackcurl /api/video-coverage | jq '.videos | length'should return ~1083🤖 Generated with Claude Opus 4.6 (1M context) via Claude Code